// Virtual Machine: vm.c
// 5.4.08

#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include "vm.h"

#define MAX_LINE_LEN 300

void vm_make(FILE *in, FILE *err, struct vm_State *vm)
{
	// initialize variables for getting string tokens
   	char s[MAX_LINE_LEN];
   	int ln = 0;
   	int lnchange = 1;
	char *d;

   while (fgets(s, MAX_LINE_LEN, in) != NULL) // read each line
   {
		int i;
		 if(strstr(s, ";") != NULL)
			lnchange = 0;
        for ( i = 0; i < strlen(s); i++ ) //removes '\n' char from end of line
        {
			if ( s[i] == '\n' || s[i] == '\0' || s[i] == ';')
			{
                s[i] = ' ';
            }
		}
       
        lex(err, s, ln, vm); // send line to lexer
		 if(lnchange == 0)
        {
            ln +=1;
            lnchange = 1;
        }
	}
}

void vm_execute(FILE *in, FILE *out, FILE *err, struct vm_State *vm)
{
		if(vm->head == NULL)
		{
			fprintf(err, "Error: There is no file\n");
			exit(0);
		}
		
		struct node *pos = vm->head;
        int lp = 0;
        int lpnum = 0;
		int run_vm = 1;
        int n = 1;

		// if statements to check for each command
		if(pos->type == OPCODE && pos->v.op == BEGIN)
		{
			pos = pos->next;
			n += 1;
			while(run_vm != 0)
			{
				if(pos == NULL && run_vm != 0)
				{
					fprintf(err, "END was not called at the end of program.\n");
					vm_delete(vm);
					exit(0);
				}
                       if(lp > 0 && pos->linenum != lp)
                       {
                               if(lpnum > 1)
                               {
                                       pos = pos_update(pos, lp);
                                       lpnum--;
                               }
                               else
                                       lp = 0;
                       }
                       if(pos->type == OPCODE)
                       {
                               if(pos->v.op == MOV)
                               {
                                       if(pos->next->type == INTEGER && pos->next->next->type == FLOAT)
                                       {
                                               move (pos->next->v.i, pos->next->next->v.f, vm);
                                               pos = pos->next->next->next;
                                       }
                                       else
										vm_error(err, n, vm);
                               }
                               else if(pos->v.op == ADD)
                               {
                                       if(pos->next->type == INTEGER && pos->next->next->type == INTEGER)
                                       {
                                               add (pos->next->v.i, pos->next->next->v.i, vm);
                                               pos = pos->next->next->next;
                                       }
                                       else
                                              vm_error(err, n, vm);
                               }
                               else if(pos->v.op == SUB)
                               {
                                       if(pos->next->type == INTEGER && pos->next->next->type == INTEGER)
                                       {
                                               sub (pos->next->v.i, pos->next->next->v.i, vm);
                                               pos = pos->next->next->next;
                                       }
                                       else
                                               vm_error(err, n, vm);
                               }
                               else if(pos->v.op == MULT)
                               {
                                       if(pos->next->type == INTEGER && pos->next->next->type == INTEGER)
                                       {
                                               mult (pos->next->v.i, pos->next->next->v.i, vm);
                                               pos = pos->next->next->next;
                                       }
                                       else
                                               vm_error(err, n, vm);
                               }
                               else if(pos->v.op == DIVD)
                               {
                                       if(pos->next->type == INTEGER && pos->next->next->type == INTEGER)
                                       {
                                               if(vm->regs[pos->next->next->v.i] == 0)
                                                       vm_error(err, n, vm);

                                               divd (pos->next->v.i, pos->next->next->v.i, vm);
                                               pos = pos->next->next->next;
                                       }
                                       else
                                               vm_error(err, n, vm);
                               }
                               else if(pos->v.op == SWAP)
                               {
                                       if(pos->next->type == INTEGER && pos->next->next->type == INTEGER)
                                       {
                                               swap (pos->next->v.i, pos->next->next->v.i, vm);
                                               pos = pos->next->next->next;
                                       }
                                       else
                                               vm_error(err, n, vm);
                               }
                               else if(pos->v.op == COPY)
                               {
                                       if(pos->next->type == INTEGER && pos->next->next->type == INTEGER)
                                       {
                                               copy (pos->next->v.i, pos->next->next->v.i, vm);
                                               pos = pos->next->next->next;
                                       }
                                       else
                                               vm_error(err, n, vm);
                               }
                               else if(pos->v.op == CLR)
                               {
                                       if(pos->next->type == INTEGER)
                                       {
                                               clr (pos->next->v.i, vm);
                                               pos = pos->next->next;
                                       }
                                       else
                                               vm_error(err, n, vm);
                               }
                               else if(pos->v.op == DISPR)
                               {
                                       if(pos->next->type == INTEGER)
                                       {
                                               dispr (out, pos->next->v.i, vm);
                                               pos = pos->next->next;
                                       }
                                       else
                                              vm_error(err, n, vm);
                               }
                               else if(pos->v.op == DISPS)
                               {
                                       if(pos->next->type == CHAR)
                                       {
                                              fprintf (out, "%s\n", pos->next->v.ch);
                                               pos = pos->next->next;
                                       }
                                       else
                                               vm_error(err, n, vm);
                               }
                               else if(pos->v.op == GET)
                               {
                                       if(pos->next->type == INTEGER)
                                       {
                                               get (in, pos->next->v.i, vm);
                                               pos = pos->next->next;
                                       }
                                       else
                                               vm_error(err, n, vm);
                               }
                               else if(pos->v.op == COMP)
                               {
                                       if(pos->next->type == INTEGER && pos->next->next->type == INTEGER)
                                       {
                                               int j = comp (pos->next->v.i, pos->next->next->v.i, vm);
                                               if(j == 1)
                                                       pos = pos->next->next->next;
                                               else
                                                       pos = pos->next->next->next->next;
                                       }
                                       else
                                               vm_error(err, n, vm);
                               }
                               else if(pos->v.op == LOOP)
                               {
                                       if(pos->next->type == INTEGER)
                                       {
                                               lp = pos->linenum;
                                               lpnum = vm->regs[pos->next->v.i];
                                               pos = pos->next->next;
                                       }
                                       else
                                            vm_error(err, n, vm);
                               }
							   else if(pos->v.op == END)
									run_vm = 0;
							   else
                                      vm_error(err, n, vm);
                       }
                       else
                               vm_error(err, n, vm);
                       //pos = pos->next;
					   if(lp == 0)
						n += 1;
            }
       }
		else
			vm_error(err, n, vm);
}

void vm_delete(struct vm_State *vm)
{
       //free(vm.regs);
       delete_list(vm);
}

struct node *pos_update(struct node *pos, int lp)
{
       while(pos->linenum >= lp)
       {
               pos = pos->prev;
       }
       pos = pos->next->next->next;
}

void vm_error(FILE *err, int ln, struct vm_State *vm)
{
       fprintf(err, "There was an error at line %d \n", ln);
		vm_delete(vm);
       exit(0);
}

// Function List: move, add, sub , mult, div, swap, copy, clr, dispr, get, comp

// Move Function: put a value into certain register location
void move (int  a, float f, struct vm_State *vm)
{
	vm->regs[a]= f;
}

// Add Function: adds values of two registers and puts it in the first register
void add (int  a, int b, struct vm_State *vm)
{
       vm->regs[a] = vm->regs[a] + vm->regs[b];
}

// Subtract Function: finds difference of two registers and puts difference in the first register
void sub (int  a, int b, struct vm_State *vm)
{
       vm->regs[a] = vm->regs[a] - vm->regs[b];
}

// Multiply Function: multiplies two values and puts product in the first register
void mult (int  a, int b, struct vm_State *vm)
{
       vm->regs[a] = vm->regs[a] * vm->regs[b];
}

// Divide Function: divides value of first by second register and puts it in the first
void divd (int a, int b, struct vm_State *vm)
{
       vm->regs[a] = vm->regs[a] / vm->regs[b];
}

// Swap Function: swaps the values of two registers
void swap (int  a, int b, struct vm_State *vm)
{
       float temp;
       temp = vm->regs[a];
       vm->regs[a] = vm->regs[b];
       vm->regs[b] = temp;
}

// Copy Function: copys the value of one register to another
void copy (int a, int b, struct vm_State *vm)
{
       vm->regs[b] = vm->regs[a];
}

// Clear Function: clears the value of a certain register
void clr (int  a, struct vm_State *vm)
{
       vm->regs[a] = 0;
}

// Display Register Function: prints value of certain register to the screen
void dispr (FILE *out, int a, struct vm_State *vm)
{
       fprintf (out, "R%d = %.2f \n", a, vm->regs[a]);
}

// Get Function: gets a value from the user to be placed in a register
void get (FILE *in, int a, struct vm_State *vm)
{
		printf("Input Value: ");
       float t;
       scanf ("%f", &t );
       vm->regs[a] = t;
}

//Compare Function: compares two values
int comp (int a, int b, struct vm_State *vm)
{
       if(vm->regs[a] > vm->regs[b])
               return 1;
       else
               return 0;
}
